﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;



/*Alright here is how it works:
 First of all the server registers N number of CookiesJars which will be processed. Each cookie jar then will work as a container for the incoming packets (cookie pieces, which will later form a single cookie).
 Once the pieces are begin sent the cookie will start accumulating and then produce one single cookie which will be processed directly.
 In other words the cookie is a single buffer and the pieces are different packets which will be submited.
 
 
 Idenfitication and groupping of each pieces sent:
 Each cookie has it's own ID which will be generated by shifting 2 bytes right the cookie jar's ID. (Cookie Jar ID: 2, Cookie ID: 2 >> 2)
 Each piece has it's own ID which will be used to identify where the piece has to go. The ID is generated by the last Cookie ID digit.
 So if the Cookie ID is 1223, the piece ID will be 3.*/



namespace HNPL.Sockets
{
    /// <summary>
    /// Represents a Cookie Piece. (Packet data)
    /// </summary>
    public class UdpCookiePiece : EventArgs
    {
        private short cpId; // Cookie Piece ID.
        private short cpNum;  //Cookie Piece Number.
        private byte[] pInfo;  //Cookie Piece Data.

        /// <summary>
        /// The current piece ID.
        /// </summary>
        public short PieceID { get { return cpId; } }
        /// <summary>
        /// The number which represents the order of the piece.
        /// </summary>
        public short PieceNumber { get { return cpNum; } }
        /// <summary>
        /// The data containg the piece.
        /// </summary>
        public byte[] PieceInfo { get { return pInfo; } }

        /// <summary>
        /// Creates a new cookie piece.
        /// </summary>
        /// <param name="id">ID of the piece.</param>
        /// <param name="num">The number by order.</param>
        /// <param name="data">The data to be submited.</param>
        public UdpCookiePiece(short id, short num, byte[] data)
        {
            cpId = id;
            cpNum = num;
            pInfo = data;
        }

        public static UdpCookiePiece Read(byte[] data)
        {
            BinaryReader reader = new BinaryReader(new MemoryStream(data));
            short cpId = reader.ReadInt16();
            short cpNum = reader.ReadInt16();

            int len = reader.ReadInt32();
            byte[] info = reader.ReadBytes(len);

            return new UdpCookiePiece(cpId, cpNum, info);
        }

        public byte[] ToArray()
        {
            MemoryStream memsr = new MemoryStream();
            BinaryWriter writer = new BinaryWriter(memsr);

            writer.Write(cpId);
            writer.Write(cpNum);
            writer.Write(pInfo.Length);
            writer.Write(pInfo);
            writer.Flush();

            return memsr.ToArray();
        }
    }

    /// <summary>
    /// Represents a chocolate UDP cookie.
    /// </summary>
    public class UdpCookie : EventArgs
    {
        private List<UdpCookiePiece> pcs;  //A list of the current cookie pieces received.
        private int cId;  //Cookie ID.
        private int mLen; //Maximum length

        /// <summary>
        /// The maximum cookie pieces this cookie can contain.
        /// </summary>
        public int MaximumLength { get { return mLen; } }
        /// <summary>
        /// The ID of the cookie.
        /// </summary>
        public int CookieID { get { return cId; } }
        /// <summary>
        /// The list of pieces acumulated.
        /// </summary>
        public List<UdpCookiePiece> Pieces { get { return pcs; } }

        /// <summary>
        /// Creates a new cookie.
        /// </summary>
        /// <param name="id">The ID of the cookie.</param>
        public UdpCookie(int id)
        {
            cId = id;
            pcs = new List<UdpCookiePiece>();
        }

        /// <summary>
        /// Sets the maximum pieces capacity.
        /// </summary>
        /// <param name="nmLen"></param>
        public void SetMaximumLength(int nmLen)
        {
            mLen = nmLen;
        }

        /// <summary>
        /// Adds a piece to the collection.
        /// </summary>
        /// <param name="piece"></param>
        public void AddPiece(UdpCookiePiece piece)
        {
            Log.Info("Adding cookie piece, ID: " + piece.PieceID.ToString() + ", Number: " + piece.PieceNumber.ToString());
            pcs.Add(piece);
        }

        /// <summary>
        /// Consumes the current cookie information.
        /// </summary>
        /// <param name="action"></param>
        /// <param name="prms"></param>
        /// <returns></returns>
        public object Eat(Delegate action, object[] prms)
        {
            Log.Info("Eating cookie, ID: " + cId.ToString());
            return action.DynamicInvoke(prms);
        }

        public byte[] Assemble()
        {
            var s = from x in pcs orderby x.PieceNumber ascending select x;
            UdpCookiePiece[] p = s.ToArray();


            MemoryStream memsr = new MemoryStream();
            BinaryWriter writer = new BinaryWriter(memsr);
            foreach (var pp in p)
            {
                writer.Write(pp.PieceInfo);
                writer.Flush();
            }
            return memsr.ToArray();
        }
    }

    //CookieJar doesn't do shit.
    public class CookieJar
    {

    }
}
